package ar.uba.dc.tl.mylanga.semantic;

import ar.uba.dc.tl.mylanga.exception.UndefinedVariableReferenceException;
import ar.uba.dc.tl.mylanga.node.*;
import ar.uba.dc.tl.mylanga.symbol.Symbol;
import ar.uba.dc.tl.mylanga.symbol.SymbolFactory;
import ar.uba.dc.tl.mylanga.symbol.SymbolTable;

public class UndefinedVariableReferenceSemanticRule extends SemanticRule {

	private SymbolTable symbolTable;
	private SymbolFactory symbolFactory;

	public UndefinedVariableReferenceSemanticRule(PProgram program) {
		super("UndefinedVariableReferenceRule", program);
	}

	@Override
	public void check() {
		this.symbolTable = new SymbolTable();
		this.symbolFactory = new SymbolFactory();

		this.getProgram().apply(this);
	}

    @Override
    public void caseAVariableNumericExpression(AVariableNumericExpression node) {
        TIdentifier identifier = node.getIdentifier();
        String variableName = identifier.getText();
        Symbol variableSymbol = this.symbolFactory.get(variableName);

        if (!this.symbolTable.contains(variableSymbol)) {
            throw new UndefinedVariableReferenceException(identifier);
        }
    }

    @Override
    public void caseAPlotCommand(APlotCommand node) {
        TIdentifier parameter = node.getParameter();
        Symbol parameterSymbol = this.symbolFactory.get(parameter.getText());

        this.symbolTable.put(parameterSymbol);

        super.caseAPlotCommand(node);
    }

    @Override
    public void caseAFunctionDefinition(AFunctionDefinition node) {
        this.symbolTable.createScope();

        super.caseAFunctionDefinition(node);

        this.symbolTable.discardScope();
    }

    @Override
    public void caseAParameter(AParameter node) {
        Symbol parameterSymbol = this.symbolFactory.get(node.getName().getText());

        this.symbolTable.put(parameterSymbol);
    }

    @Override
    public void caseAAssignmentStatement(AAssignmentStatement node) {
        super.caseAAssignmentStatement(node);

        Symbol lhs = this.symbolFactory.get(node.getLeftValue().getText());

        this.symbolTable.put(lhs);
    }

    @Override
    public void caseAIfThenElseStatement(AIfThenElseStatement node) {
        node.getCondition().apply(this);

        this.symbolTable.createScope();
        node.getThenStatements().apply(this);
        this.symbolTable.discardScope();

        this.symbolTable.createScope();
        node.getElseStatements().apply(this);
        this.symbolTable.discardScope();
    }

    @Override
    public void caseAIfThenStatement(AIfThenStatement node) {
        node.getCondition().apply(this);

        this.symbolTable.createScope();
        node.getThenStatements().apply(this);
        this.symbolTable.discardScope();
    }

    @Override
    public void caseAWhileStatement(AWhileStatement node) {
        node.getCondition().apply(this);

        this.symbolTable.createScope();
        node.getBody().apply(this);
        this.symbolTable.discardScope();
    }
}
